package com.example.hometown_walker.util.json;

import com.example.hometown_walker.util.json.serialize.DateJsonDeserializer;
import com.example.hometown_walker.util.json.serialize.DateJsonSerializer;
import com.example.hometown_walker.util.json.serialize.SqlDateJsonDeserializer;
import com.example.hometown_walker.util.json.serialize.SqlDateJsonSerializer;
import com.example.hometown_walker.util.json.serialize.SqlTimeJsonDeserializer;
import com.example.hometown_walker.util.json.serialize.SqlTimeJsonSerializer;
import com.example.hometown_walker.util.json.serialize.SqlTimestampJsonDeserializer;
import com.example.hometown_walker.util.json.serialize.SqlTimestampJsonSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.tuple.Pair;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: jackson--Json序列化工具类
 * @author: chenchaoran
 * @date: 2020-04-02
 */
public class JsonUtil {
    private JsonUtil() {}

    public static final ObjectMapper objectMapper = new ObjectMapper();

    static {
        init(objectMapper);
    }

    public static void init(ObjectMapper objectMapper) {
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        objectMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        SimpleModule module = new SimpleModule("DateTimeModule", Version.unknownVersion());
        module.addSerializer(Date.class, new DateJsonSerializer());
        module.addDeserializer(Date.class, new DateJsonDeserializer());
        // sql
        module.addSerializer(java.sql.Date.class, new SqlDateJsonSerializer());
        module.addDeserializer(java.sql.Date.class, new SqlDateJsonDeserializer());
        module.addSerializer(java.sql.Time.class, new SqlTimeJsonSerializer());
        module.addDeserializer(java.sql.Time.class, new SqlTimeJsonDeserializer());
        module.addSerializer(java.sql.Timestamp.class, new SqlTimestampJsonSerializer());
        module.addDeserializer(java.sql.Timestamp.class, new SqlTimestampJsonDeserializer());
        // 兼容 jackson 2.5 以下的版本, 对 Map.Entry 序列化做特殊处理
        module.addSerializer(Map.Entry.class, new JsonSerializer<Map.Entry<Object, Object>>() {
            @Override
            public void serialize(Map.Entry entry, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeObject(Pair.of(entry.getKey(), entry.getValue()));
            }
        });
        objectMapper.registerModule(module);// 自定义类型序列化

        GuavaModule guavaModule = new GuavaModule(); // jackson.datatype.guava
        objectMapper.registerModule(guavaModule);// 支持guava工具包类型序列号

        JavaTimeModule javaTimeModule = new JavaTimeModule(); // jackson.datatype.jsr310
        objectMapper.registerModule(javaTimeModule);// 支持jdk8 Date/Time类型序列化
    }

    public static ObjectMapper instance() {
        return objectMapper;
    }

    public static String of(Object o) {
        try {
            return objectMapper.writeValueAsString(o);
        } catch (JsonProcessingException e) {
            throw Throwables.propagate(e);
        }
    }

    public static <T> T of(String json, Class<T> tClass) {
        if (Strings.isNullOrEmpty(json)) {
            return null;
        }
        try {
            return objectMapper.readValue(json, tClass);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    public static <T> T of(String json, TypeReference<T> reference) {
        if (Strings.isNullOrEmpty(json)) {
            return null;
        }
        try {
            return objectMapper.readerFor(reference).readValue(json);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    public static <T> T of(String json, Class<T> clazz, Module module) throws IOException {
        if (Strings.isNullOrEmpty(json)) {
            return null;
        }
        try {
            objectMapper.registerModule(module);
            return objectMapper.readerFor(clazz).readValue(json);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    public static <T> List<T> ofList(String json, Class<T> tClass) {
        if (Strings.isNullOrEmpty(json)) {
            return null;
        }
        JavaType javaType =
                objectMapper.getTypeFactory().constructParametrizedType(ArrayList.class, ArrayList.class, tClass);
        try {
            return objectMapper.readValue(json, javaType);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    public static <K, V> Map<K, V> ofMap(String json, Class<K> keyClass, Class<V> valueClass) {
        if (Strings.isNullOrEmpty(json)) {
            return null;
        }
        MapType mapType = objectMapper.getTypeFactory().constructMapType(HashMap.class, keyClass, valueClass);
        try {
            return objectMapper.readValue(json, mapType);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    public static String toJson(Object obj) {
        if (null == obj) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Throwable t) {
            Throwables.propagate(t);
        }
        return null;
    }
}
